<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>立方体</title>
</head>

<body>
  <h3>旋转动画</h3>
  <canvas id="webgl" width="500" height="500" style="background-color: rgba(197, 194, 194, 1);"></canvas>

  <script id="vertexShader" type="x-shader/x-vertex">
      attribute vec4 a_pos;//attribute声明vec4类型变量apos
      attribute vec4 a_color;// attribute声明顶点颜色变量
      attribute vec4 a_normal;//顶点法向量变量
      uniform vec3 u_lightColor;// uniform声明平行光颜色变量

      /**uniform声明旋转矩阵变量mx、my**/
      uniform mat4 mx;//绕x轴旋转矩阵
      uniform mat4 my;//绕y轴旋转矩阵
       
      uniform vec3 u_lightPosition;// uniform声明平行光颜色变量
      varying vec4 v_color;//varying声明顶点颜色插值后变量
      void main(){
  
        gl_Position = mx * my * a_pos;

        // 顶点法向量进行矩阵变换，然后归一化
        vec3 normal = normalize((mx*my*a_normal).xyz);
        // 计算点光源照射顶点的方向并归一化
        vec3 lightDirection = normalize(vec3(gl_Position) - u_lightPosition);
        // 计算平行光方向向量和顶点法向量的点积
        float dot = max(dot(lightDirection, normal), 0.0);
        // 计算反射后的颜色
        vec3 reflectedLight = u_lightColor * a_color.rgb * dot;
        //颜色插值计算
        v_color = vec4(reflectedLight, a_color.a);
      }
  </script>

  <script id="fragmentShader" type="x-shader/x-fragment">
      precision lowp float;
      varying vec4 v_color;
      void main(){
        gl_FragColor = v_color;
      }
  </script>
  <script type="module">

    var canvas = document.getElementById("webgl");
    var gl = canvas.getContext("webgl");

    var vertexShaderSource = document.getElementById("vertexShader").innerText;
    var fragShaderSource = document.getElementById("fragmentShader").innerText;

    var program = initShader(gl, vertexShaderSource, fragShaderSource);


    // 从外部向顶点着色器内传输数据
    var aposLocation = gl.getAttribLocation(program, 'a_pos');
    var a_color = gl.getAttribLocation(program, 'a_color');
    var a_normal = gl.getAttribLocation(program, 'a_normal');
    var u_lightColor = gl.getUniformLocation(program, 'u_lightColor');
    var u_lightPosition = gl.getUniformLocation(program, 'u_lightPosition');

    /**从program对象获得旋转矩阵变量mx、my地址**/
    var mx = gl.getUniformLocation(program, 'mx');
    var my = gl.getUniformLocation(program, 'my');

    const data = new Float32Array([
      // 立方体 - 前平面    颜色
      0.5, 0.5, 0.5, 0.5, 0, 0,
      -0.5, 0.5, 0.5, 0.5, 0, 0,
      -0.5, -0.5, 0.5, 0.5, 0, 0,
      0.5, -0.5, 0.5, 0.5, 0, 0,
      // 立方体 - 后平面
      0.5, 0.5, -0.5, 0.5, 0, 0,
      -0.5, 0.5, -0.5, 0.5, 0, 0,
      -0.5, -0.5, -0.5, 0.5, 0, 0,
      0.5, -0.5, -0.5, 0.5, 0, 0,
      // 立方体 - 上平面
      0.5, 0.5, 0.5, 0.5, 0, 0,
      0.5, 0.5, -0.5, 0.5, 0, 0,
      -0.5, 0.5, -0.5, 0.5, 0, 0,
      -0.5, 0.5, 0.5, 0.5, 0, 0,
      // 立方体 - 左平面
      -0.5, 0.5, 0.5, 0.5, 0, 0,
      -0.5, 0.5, -0.5, 0.5, 0, 0,
      -0.5, -0.5, -0.5, 0.5, 0, 0,
      -0.5, -0.5, 0.5, 0.5, 0, 0,
      // 立方体 - 下平面
      -0.5, -0.5, 0.5, 0.5, 0, 0,
      -0.5, -0.5, -0.5, 0.5, 0, 0,
      0.5, -0.5, -0.5, 0.5, 0, 0,
      0.5, -0.5, 0.5, 0.5, 0, 0,
      // 立方体 - 右平面
      0.5, -0.5, 0.5, 0.5, 0, 0,
      0.5, -0.5, -0.5, 0.5, 0, 0,
      0.5, 0.5, -0.5, 0.5, 0, 0,
      0.5, 0.5, 0.5, 0.5, 0, 0,
    ]);

    /**
     *顶点法向量数组normalData
    **/
    var normalData = new Float32Array([
      0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,//z轴正方向——面1
      1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,//x轴正方向——面2
      0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,//y轴正方向——面3
      -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,//x轴负方向——面4
      0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,//y轴负方向——面5
      0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1//z轴负方向——面6
    ]);


    var buffer = gl.createBuffer();
    // 激活哪个缓存区,就将数据放在哪个缓存区(缓存区有多个)
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    // 从当前绑定的缓冲区中读取顶点数据
    // 参数1：指定要修改的顶点属性的索引
    // 参数2：指定每个顶点属性的组成数量
    // 参数3: 元素的数据类
    // 参数5：每一单元所占字节数
    // 参数6：偏移字节数
    gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, 4 * 6, 0);//设置点为2位
    gl.enableVertexAttribArray(aposLocation);

    gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 6, 4 * 3);//设置点为2位
    gl.enableVertexAttribArray(a_color);


    var normalBuffer = gl.createBuffer();
    // 激活哪个缓存区,就将数据放在哪个缓存区(缓存区有多个)
    gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, normalData, gl.STATIC_DRAW);

    gl.vertexAttribPointer(a_normal, 3, gl.FLOAT, false, 0, 0);//设置点为2位
    gl.enableVertexAttribArray(a_normal);


    /**
     * 给平行光传入颜色和方向数据，RGB(1,1,1),单位向量(x,y,z)
     **/
    gl.uniform3f(u_lightColor, 1.0, 0.0, 0.0);
    //保证向量(x,y,-z)的长度为1，即单位向量
    // 如果不是单位向量，也可以再来着色器代码中进行归一化
    var x = 1 / Math.sqrt(14), y = 2 / Math.sqrt(14), z = 3 / Math.sqrt(14);
    gl.uniform3f(u_lightPosition, x, y, -z);


    /**执行绘制之前，一定要开启深度测试，以免颜色混乱**/
    gl.enable(gl.DEPTH_TEST);


    /**绕x轴旋转45度**/
    var mxArr = new Float32Array([
      1, 0, 0, 0,
      0, Math.cos(Math.PI / 4), -Math.sin(Math.PI / 4), 0,
      0, Math.sin(Math.PI / 4), Math.cos(Math.PI / 4), 0,
      0, 0, 0, 1
    ]);
    //把数据mxArr传递给着色器旋转矩阵变量mx
    gl.uniformMatrix4fv(mx, false, mxArr);


    /**
     * 定义绘制函数draw()，定时更新旋转矩阵数据，并调用WebGL绘制API
     ***/
    var angle = Math.PI / 4;//起始角度
    function draw() {
      // gl.clear(gl.COLOR_BUFFER_BIT);//清空画布上一帧图像
      /**
       * 立方体绕y轴旋转
       ***/
      angle += 0.01;//每次渲染角度递增，每次渲染不同的角度
      var sin = Math.sin(angle);//旋转角度正弦值
      var cos = Math.cos(angle);//旋转角度余弦值
      var myArr = new Float32Array([cos, 0, -sin, 0, 0, 1, 0, 0, sin, 0, cos, 0, 0, 0, 0, 1]);
      gl.uniformMatrix4fv(my, false, myArr);
     
      /**执行绘制命令**/
      // gl.drawArrays(gl.TRIANGLES, 0, 36);
      // 6个面
      for (var i = 0; i <= 6; i++) {
        gl.drawArrays(gl.TRIANGLE_FAN, 4 * i, 4);//绘制矩形边框
      }
      requestAnimationFrame(draw);
    }
    draw();



    // gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);//绘制矩形边框
    // gl.drawArrays(gl.TRIANGLE_FAN, 4, 4);//绘制矩形边框
    // gl.drawArrays(gl.TRIANGLE_FAN, 8, 4);//绘制矩形边框
    // gl.drawArrays(gl.TRIANGLE_FAN, 12, 4);//绘制矩形边框
    // gl.drawArrays(gl.TRIANGLE_FAN, 16, 4);//绘制矩形边框
    // gl.drawArrays(gl.TRIANGLE_FAN, 20, 4);//绘制矩形边框
    // gl.drawArrays(gl.TRIANGLE_FAN, 24, 4);//绘制矩形边框


    function initShader(gl, vertexShaderSource, fragShaderSource) {
      //创建顶点着色器对象
      var vertexShader = gl.createShader(gl.VERTEX_SHADER);
      //创建片元着色器对象
      var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
      //引入顶点、片元着色器源代码
      gl.shaderSource(vertexShader, vertexShaderSource);
      gl.shaderSource(fragmentShader, fragShaderSource);
      //编译顶点、片元着色器
      gl.compileShader(vertexShader);
      gl.compileShader(fragmentShader);

      //创建程序对象program
      var program = gl.createProgram();
      //附着顶点着色器和片元着色器到program
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      //链接program
      gl.linkProgram(program);
      //使用program
      gl.useProgram(program);
      //返回程序program对象
      return program;
    }
  </script>
</body>

</html>
